var EXPORTED_SYMBOLS = ["ParseHandler"];

const LOCATION = "chrome://on-the-wave/content/PTTParser.js";
const LINEDIFF = 110;

var BBS = tabulator.ns.bbs; //the namespace for generating symbols we 
                            //defined, use, say, BBS('score')
//the rests are to be used in the say way, but they are standardized terms.
var RDF = tabulator.ns.rdf; //for rdf:type
var DCTERMS = tabulator.ns.dcterms; //for dcterms:title
var SIOC = tabulator.ns.sioc; //for sioc:has_container, sioc:has_owner, etc
                              //see <http://rdfs.org/sioc/spec/>
var RDFS = tabulator.ns.rdfs; // for rdfs:seeAlso
var FOAF = tabulator.ns.foaf; // for foaf:primaryTopic

var OWL = tabulator.ns.owl;
kb.add(FOAF('primaryTopic'), RDF('type'), OWL('FunctionalProperty'));
var gUserRegistered = {}; //sioc:User regiestered in kb

var tester = {
	test : function test(handler) {
    handler.parse_post();
		this.check_any('board_name');
		this.check_any('post_title');
		this.check_any('post_time');
	},
	
	check_any : function (handler, arg1) {
	  	var rel = kb.sym(arg1);
		var ret = kb.any(undefined, rel, handler.symbol);
		if (!ret) {
			alert('Check ' + arg1 + 'with result: None');
		} else {
			alert('Check ' + arg1 + 'with result: ' + ret.uri);
		}  		
	},
}

var util = {
	mLog : '',
	Log : function (str) {
		this.mLog += str;
	},
	getLog : function () {
		var tmp = this.mLog;
		this.mLog = '';
		return tmp;
	},
	vocabulary: {
		post_title: "http://purl.org/dc/terms/title",
		post_time: "http://purl.org/dc/terms/created",
		board_name: "http://code.google.com/p/on-the-wave/voc#board_name",
		author_id: "http://code.google.com/p/on-the-wave/voc#author_id",
		author_nickname: "http://code.google.com/p/on-the-wave/voc#author_nickname",
		id_post_content: "http://code.google.com/p/on-the-wave/voc#id_post_content"
	},
	
	add_to_kb: function (handler, arg1, arg2) {
    	//alert("add to kb: " + arg1 + " + " + arg2);    	
	    kb.add(handler.symbol, kb.sym(this.vocabulary[arg2]), kb.literal(arg1));
	}
};

function ParseHandler() {
	this.state = '';
	this.symbol = null;
	this.buffer = "";
	this.post_content = "";	
};

ParseHandler.prototype = {
  tag_value : function (tag) {
    var trim = /[^\s]+/;
    tag = trim.exec(tag);
    if (tag == "作者") return 0;
    if (tag == "看板") return 1;
    if (tag == "標題") return 2;
    if (tag == "時間") return 3;
  },
	
  parse_post : function (){
    function handle_post(tag) {   
      if (this.state == '') {     
        switch (this.tag_value(tag)) {
          case this.tag_value("作者"):
            this.state = 'author_id;author_nickname'; 
            break;
          case this.tag_value("看板"):          
            this.state = 'board_name'; 
            break;
          case this.tag_value("標題"):
            this.state = 'post_title'; 
            break;
          case this.tag_value("時間"):
            this.state = 'post_time'; 
            break;
          default:
            this.state = '';
            break;
        }
      } else if (this.state == 'id_post_content'){
        this.post_content += tag;
      } else {
        var ids = this.state.split(';');
        if (ids.length > 1) {
          var template = /^(.*?)\((.*?)\)/; //Case author (nickname)
          var tags = template.exec(tag);
          if (tags) {
            util.add_to_kb(this, tags[1], ids[0]);
            util.add_to_kb(this, tags[2], ids[1]);
          // kb.add(kb.sym(tags[0]), kb.sym(ids[0]), this.symbol);
          // kb.add(kb.sym(tags[1]), kb.sym(ids[1]), this.symbol);
          }             
        } else {
          util.add_to_kb(this, tag, this.state);        
        } 
        if (this.state == 'post_time') {
          this.state = 'id_post_content';
          this.post_content = "";
        } else {
          this.state = '';
        }         
      }
    } 
    try{
			// Remove redundant page footnote
      this.buffer = this.buffer.replace(
			    /\x1B\[.*m\s*瀏覽\s*第\s*\d+\/\d+\s*頁\s*\(\s*\d+%\).*?\d+~\d+(.*?離開)?/g , '');
	
	    var i = this.buffer.indexOf("[34;47m 作者");
	    var ansi = this.buffer.substr(i); //remove misc info before 作者
	    
	    //var ansi = FireBBS.previous_ansi;
	    ansi = ansi.replace(/\x07/g, '\x1B\x07');//escape BEL
	    ansi = ansi.replace(/[\b]/g, '\x1B[1D');//escape BS(replace )
	    ansi = ansi.replace(/\t/g, '\x1B[8C');//escape HT(replace )
	    ansi = ansi.replace(/\n/g, '\x1B\n');//escape LF
	    ansi = ansi.replace(/\v/g, '\x1B[6B');//escape VT(replace )
	    ansi = ansi.replace(/\f/g, '\x1B[2J\x1B[H');//escape FF(replace )
	    ansi = ansi.replace(/\r/g, '\x1B[1G');//escape CR(replace )
	    
	    var strArray = ansi.split('\x1B');
	    for (var i = 0; i < strArray.length; i++) {
	      switch (strArray[i][0]) {
	        case '['://Functions using CSI
	          var template = /^\x5B((?:|\d+|;)+)([\x40-\x5A\x60-\x7C])/;//Functions using CSI
	          var result = template.exec(strArray[i]);
	          if(result){             
	            handle_post.call(this, strArray[i].substr(result[0].length));
	          }
	          break;
	        case '\n':
	          handle_post.call(this, "<br>" + strArray[i].substr(1));
	          break;
	      }
	    }
	    // Add the post content to kb.        
	    util.add_to_kb(this, this.post_content, 'id_post_content');
    } catch (e) { 
		  handleException(e, LOCATION, LINEDIFF); 
    }
  },
  
  parse_postlist: function parse_postlist(aStr){
    //aStr is conn.mScreenString sent from the BBS_BOARD state
    try{
      var theBoard = this.symbol;
      //a board is a forum
      kb.add(theBoard, RDF('type'), SIOC('Forum'));

      //讀出板主
      var boardOwnersStr = aStr.substring(aStr.indexOf("板主:")+3,
                                          aStr.indexOf("】"));
      var boardOwnersStrArray = boardOwnersStr.split('/');
      for (var i=0; i<boardOwnersStrArray.length; i++)
        kb.add(theBoard, SIOC('has_owner'), kb.literal(boardOwnersStrArray[i]));
      //@@板主有...的時候

      aStr = aStr.substr(aStr.indexOf("】") + 1); //scan next 

      //讀出板標
      var boardNameRegexp = new RegExp("看板《.+?》 \n");
      var boardNameStart = aStr.search(boardNameRegexp);
      kb.add(theBoard, DCTERMS('title'), aStr.substr(0, boardNameStart).trim());

      aStr = aStr.substr(aStr.indexOf('\n') + 1); //scan next

      //讀出人氣
      var visitorsRegexp = new RegExp("人氣:(\\d+)   \n");
      kb.add(theBoard, BBS('currentVisitors'), parseInt(visitorsRegexp.exec(aStr)[1]));

      //讀出剩下的文章資料
      var articleLines = aStr.split('\n');
			
      //去除開頭兩行結束一行
      articleLines.shift();
			articleLines.shift();
			articleLines.pop();
      var firstArticleNumber = 0; //第一篇文章的編號 決定要不要繼續往上讀
      var starsNumber = 0; //置底文章數目
      var lastIndex = 0; //讀到文章的編號
      for (var line = articleLines.shift(); line; line = articleLines.shift()){
        if (!line.trim()) break; //空行
        var articleDataRegexp = new RegExp("^(?:●|  )(  ★ | *\\d+) ([ +~mMsS=!])([\\d ][\\d ]|爆|XX|X\\d)([\\d ]\\d/\\d\\d) (\\S+)(.+)$");
        var result = articleDataRegexp.exec(line);
        //@不要用正規表示比較快(?)

        var aArticle = kb.bnode(); //the node of the article
        kb.add(aArticle, SIOC('has_container'), theBoard);
			  
        //文章的特殊狀況 被刪或是M文或是置底文
        var status = "";
        if (result[2] === "m" || result[2] === "M"){
          status = 'elected';
        } else if (result[5] === "-") {
          status = 'deleted';
        } else if (result[1] === "  ★ ") {
          status = 'important';
          starsNumber++;
        }
        if (status) kb.add(aArticle, BBS('status'), status);

        if (status !== 'important'){
          lastIndex = parseInt(result[1]);
          if (!firstArticleNumber) firstArticleNumber = lastIndex;
          kb.add(aArticle, BBS('currentNumber'), lastIndex);
        } else {
          //給置底文章一個*假的*currentNumber以便post_list.js排序
          //@全部是★的情形
          kb.add(aArticle, BBS('currentNumber'), lastIndex + starsNumber);
        }
  
        //用一個*不太明確*的數字表示被推爆或是噓爆的文的推文數
        if (result[3] === "  "){
          kb.add(aArticle, BBS('score'), 0);
        } else if (result[3] === "爆"){
          kb.add(aArticle, BBS('score'), 100);
        } else if (result[3][0] === "X") {
          if (result[3][1] === "X")
            kb.add(aArticle, BBS('score'), -100);
          else
            kb.add(aArticle, BBS('score'), -10 * parseInt(result[3][1]));
        } else {
          kb.add(aArticle, BBS('score'), parseInt(result[3]));
        }

        //把日期變成W3CDTF的標準格式
        var MD = result[4].split('/');
        var date = new Date();
        date.setMonth(parseInt(MD[0]));
        date.setDate(parseInt(MD[1]));
        kb.add(aArticle, DCTERMS('created'), W3CDTF_create(date));
        //@@這裡事實上是錯誤的時間 要考慮台灣的時間 != 世界的絕對時間

        
        var userURI = "http://" + theBoard.uri.split('/')[2] + '/' +
                      "~users/" + result[5]; //result[5] need not to trim
        var user = kb.sym(userURI);
        if (!gUserRegistered[userURI]){
          gUserRegistered[userURI] = true;
          kb.add(user, RDF('type'), SIOC('User'));
          kb.add(user, FOAF('accountName'), result[5]);
        }
          
        kb.add(aArticle, SIOC('has_creator'), user); 

        //找出標題..@@注意到這個標題可能和內文不同...
        kb.add(aArticle, DCTERMS('title'), result[6].trim());
        //@@文章標題有...的時候
			
        //Meta data for identifying this article (Finding it's URI)
        if (result[5] !== "-" && !starsNumber){
          //<bbs://ptt.cc/Gossiping/12345>, say
          var aDoc = kb.sym(theBoard.uri + lastIndex.toString());
          kb.add(aArticle, RDFS('seeAlso'), aDoc);
          kb.add(aDoc, FOAF('primaryTopic'), aArticle);
        }
      }
      return firstArticleNumber;
    } catch (e) { 
      handleException(e, LOCATION, LINEDIFF); 
    }    
  },
	
	//called by the BBS connection
  feed: function feed(aStr){
    this.buffer += aStr;
  },
 
  flush: function flush(){
    this.parse_post();    
    this.buffer = "";
  }
}



//modified from <resource://gre/modules/ISO8601DateUtils.jsm>
function W3CDTF_create(aDate){
  function zeropad (s, l) {
    s = s.toString(); // force it to a string
    while (s.length < l) {
      s = '0' + s;
    }
    return s;
  }
  var result = zeropad(aDate.getUTCFullYear (), 4) +
               zeropad(aDate.getUTCMonth () + 1, 2) +
               zeropad(aDate.getUTCDate (), 2) + 'T' +
               zeropad(aDate.getUTCHours (), 2) + ':' +
               zeropad(aDate.getUTCMinutes (), 2) + ':' +
               zeropad(aDate.getUTCSeconds (), 2) + 'Z';
  
  return result;
}
